package com.itheima.controller;

import com.itheima.entity.User1;
import com.itheima.service.User1Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.*;

import java.util.List;




/*
    Spring Cache 介绍

    Spring Cache是一个缓存框架，实现了基于注解的缓存功能，只需要简单地加一个注解，就能实现缓存功能,解决数据库缓存不一致问题.
    Spring Cache提供了一层抽象，底层可以切换不同的cache实现,具体使用哪种缓存技术取决于pom文件中导入的依赖。具体就是通过CacheManager接口来统一不同的缓存技术。
    CacheManager是Spring提供的各种缓存技术抽象接口。

    针对不同的缓存技术需要实现不同的CacheManager：
    CacheManager	    描述
    EhCacheCacheManager	使用EhCache作为缓存技术
    GuavaCacheManager	使用Google的GuavaCache作为缓存技术
    RedisCacheManager	使用Redis作为缓存技术
     */

/**
 * Spring Cache 常用注解
 * <p>
 * 注解	            说明
 *
 * @EnableCaching 开启缓存注解功能 (启动类上)
 * @Cacheable 在方法执行前spring先查看缓存中是否有数据，如果有数据，则直接返回缓存数据,不执行方法；若没有数据，调用方法并将方法返回值放到缓存中 (方法上)
 * @CachePut 将方法的返回值放到缓存中 (方法上)
 * @CacheEvict 将一条或多条数据从缓存中删除 (方法上)(evict /ɪˈvɪkt/ v.驱逐)
 * <p>
 * 在spring boot项目中，使用缓存技术只需在项目中导入相关缓存技术的依赖包，并在启动类上使用@EnableCaching开启缓存支持即可。
 * 例如，使用Redis作为缓存技术，只需要导入Spring data Redis的maven坐标即可。
 */
/*
在Spring Boot项目中使用Spring Cache的操作步骤(使用redis缓存技术)：
1、导入maven坐标
	spring-boot-starter-data-redis、spring-boot-starter-cache
2、配置application.yml
spring:
    cache:
        redis:
            time-to-live: 1800000 #设置缓存有效期 (非必须)
3、在启动类上加入@EnableCaching注解，开启缓存注解功能
4、在Controller的方法上加入@Cacheable、@CacheEvict等注解，进行缓存操作

 */
@RestController
@RequestMapping("/user")
public class User1Controller_SpringCache框架笔记 {
    @Autowired
    private User1Service user1Service;


    @Autowired
    private CacheManager cacheManager;


    /*
    @CachePut:将方法返回值放入缓存
    value:缓存的名称,每个缓存名称下面可以有多个key
    key:缓存的key
     */
    @CachePut(value = "userCache", key = "#user1.id") /*#result 代表方法返回值,#root代表方法自身 (ctrl+点击key查看注释)*/
    @PostMapping
    public User1 save(User1 user1) {
        user1Service.save(user1);
        return user1;
    }


    /*
    @CacheEvict:清理指定缓存
    value:缓存的名称,每个缓存名称下面可以有多个key
    key:缓存的key
     */
    @CacheEvict(value = "userCache", key = "#id")
//    @CacheEvict(value = "userCache",key = "#p0")//第0个参数,同上
//    @CacheEvict(value = "userCache",key = "#root.args[0]")//第0个参数,同上
    @DeleteMapping("/{id}")
    public void delete(@PathVariable Long id) {
        user1Service.removeById(id);
    }


    /*@CacheEvict:清理指定缓存  同上*/
    @CacheEvict(value = "userCache", key = "#user1.id")
//    @CacheEvict(value = "userCache",key = "#p0.id") //同上
//    @CacheEvict(value = "userCache",key = "#root.args[0].id") //同上
//    @CacheEvict(value = "userCache",key = "#result.id") //同上
    @PutMapping
    public User1 update(User1 user1) {
        user1Service.updateById(user1);
        return user1;
    }

    /*
        @Cacheable:在方法执行前spring先查看缓存中是否有数据，如果有数据，则直接返回缓存数据,不执行方法；若没有数据，调用方法并将方法返回值放到缓存中
        value:缓存的名称,每个缓存名称下面可以有多个key
        key:缓存的key
         */
        /*当数据库中没有查询到结果,返回值为null时,也使用id作为key,null作为value往缓存插入了一条数据

        可以使用@Cacheable的condition和unless属性进行处理

        condition:满足某条件时才往缓存中存数据(condition不可以用#result,unless可以)
        unless:除非满足某条件才不往缓存中存数据,否则都会往缓存中存数据
        */
    @Cacheable(value = "userCache", key = "#id", unless = "#result == null")
    @GetMapping("/{id}")
    public User1 getById(@PathVariable Long id) {
        User1 user1 = user1Service.getById(id);
        return user1;
    }


    @Cacheable(value = "userCache", key = "#user1.id + '_' + #user1.name")
//有漏洞,按照id查询后,下次直接从缓存中取,如果中间某些数据被删除修改,则那些多条件组合查询的缓存会与数据库会不一致,解决办法是删除某条数据时,不再是只删除对应id的缓存,而是使用

    // @CacheEvict(value = "userCache",allEntries = true)

    // 删除整个userCache分组下的全部key,如果只删除某个id的缓存,会漏删多条件组合查询的缓存,导致数据库缓存不一致

    @GetMapping("/list")
    public List<User1> list(User1 user1) {
        List<User1> list = user1Service.list(user1);//根据id或者name查询
        return list;
    }


}
